package com.paradogs.framework.server;

import com.paradogs.common.utils.EventUtils;
import com.paradogs.common.utils.ProtoUtils;
import com.paradogs.common.utils.SpringUtils;
import com.paradogs.framework.annotations.ParadogsAnnotationsHolder;
import com.paradogs.framework.annotations.exceptionHandler.ParadogsExceptionHandlerManager;
import com.paradogs.framework.utils.*;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author: yumi
 * @date: 2023/12/26  17:33
 * @Description: 游戏逻辑主处理线程：单线程 +采用1单线程n虚拟线程实现
 */
@Slf4j
@Component
@EnableAsync
public class ParadogsGameLogicHandleThread implements ApplicationRunner {

    private static final ServerEvent.ServerUpdateEvent SERVER_UPDATE_EVENT = new ServerEvent.ServerUpdateEvent(new Object());

    @Getter
    private static boolean RUNNING_FLAG = true;
    private static boolean RUNNING = true;

    @Value("${server.min-world-update-time:1}")
    private long minUpdateDiff;

    // 用户 Actor 消息处理队列
    public static Map<String, LinkedBlockingQueue<ParadogsContext>> actorQueueMap = new ConcurrentHashMap<>();
    private static final Map<String, Boolean> actorQueueLockMap = new ConcurrentHashMap<>();
    private static final ExecutorService virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();

    /** 增加新消息到处理线程 */
    public static void join(String key, ParadogsContext ctx) {
        if (!actorQueueMap.containsKey(key)) {
            actorQueueMap.put(key, new LinkedBlockingQueue<>());
        }
        actorQueueMap.get(key).add(ctx);
    }
    public static void join(String key, Runnable exec) { join(key, new ParadogsContext(exec)); }

    /** 执行路由方法 */
    private void handleByRoute(ParadogsContext ctx) {

        Method handle = ParadogsAnnotationsHolder.PRHandlerMap.get(ctx.getRoute());
        if (handle == null) {
            throw new RuntimeException("Not found route: " + ctx.getRoute());
        }

        Object handlerObject = SpringUtils.getBean(handle.getDeclaringClass()); // 处理类的实体 bean
        if (handlerObject == null) {
            throw new RuntimeException("Not Register HandlerClass: " + handle.getDeclaringClass().getName());
        }

        // 自动注入参数并执行对应路由方法：参数1 = 消息对象，参数2 = 当前上下文
        Object result = null;
        try {
            // handle before
//            Method before = ParadogsAnnotationsHolder.PRHandlerBeforeMap.get(ctx.getRoute());
//            if (before != null) {
//                Class<?>[] beforeParameterTypes = before.getParameterTypes();
//                switch (beforeParameterTypes.length) {
//                    case 0: result = before.invoke(handlerObject); break;
//                    case 1: result = before.invoke(handlerObject, ProtoUtils.decode(ctx.getMessage().getDataBytes(), beforeParameterTypes[0])); break;
//                    case 2: result = before.invoke(handlerObject, ProtoUtils.decode(ctx.getMessage().getDataBytes(), beforeParameterTypes[0]), ctx); break;
//                }
//            }
            // handle
            Class<?>[] handleParameterTypes = handle.getParameterTypes();
            Object cgData = ProtoUtils.decode(ctx.getMessage().getDataBytes(), handleParameterTypes[0]);
            ValidUtils.validateOrThrow(cgData); // 校验合法
            switch (handleParameterTypes.length) {
                case 0: result = handle.invoke(handlerObject); break;
                case 1: result = handle.invoke(handlerObject, cgData); break;
                case 2: result = handle.invoke(handlerObject, cgData, ctx); break;
            }
            // handle after
            Method after = ParadogsAnnotationsHolder.PRHandlerAfterMap.get(ctx.getRoute());
            if (after != null) {
                Class<?>[] afterParameterTypes = after.getParameterTypes();
                switch (afterParameterTypes.length) {
                    case 0: result = after.invoke(handlerObject); break;
                    case 1: result = after.invoke(handlerObject, ProtoUtils.decode(ctx.getMessage().getDataBytes(), afterParameterTypes[0])); break;
                    case 2: result = after.invoke(handlerObject, ProtoUtils.decode(ctx.getMessage().getDataBytes(), afterParameterTypes[0]), ctx); break;
                }
            }

            if (result == null) { return; }


            // 将返回值作为回复消息进行发送
            if (result instanceof CompletableFuture) {
                // 异步返回
                ((CompletableFuture<?>) result)
                        .thenAccept(gc -> MsgUtils.notifyClient(ctx.getChannel(), MsgUtils.ROUTE_GC, gc, null, ctx.getMessage().getHeader().getId()))
                        .exceptionally(e -> {
                            ParadogsExceptionHandlerManager.forward(e, ctx); // 转发到全局消息处理器
                            throw new RuntimeException(); // 中止
                        });
            } else {
                // 同步返回
                MsgUtils.notifyClient(ctx.getChannel(), MsgUtils.ROUTE_GC, result, null, ctx.getMessage().getHeader().getId());
            }

        } catch (Exception e) {
            // 转发到全局消息处理器
            ParadogsExceptionHandlerManager.forward(e, ctx);
        }

    }

    @Override
    public void run(ApplicationArguments args) {
        EventUtils.publishEvent(new ServerEvent.ServerStartEvent(this));
        virtualThreadExecutor.submit(() -> {
            /** 计算服务器下一帧时间并执行：参考 TrinityCore */
            log.info("GameLogicThread start ...");
            long realCurrTime = 0;
            long realPrevTime = System.currentTimeMillis();
            // 待优化性能 anyMatch
            while (RUNNING_FLAG || !actorQueueMap.isEmpty() && actorQueueMap.values().stream().anyMatch(it -> !it.isEmpty())) {
                realCurrTime = System.currentTimeMillis();
                long diff /* deltaTime */ = realCurrTime - realPrevTime;
                if (diff < minUpdateDiff) {
                    long sleepTime = minUpdateDiff - diff;
                    try {
                        // 等待下一帧时间，有误差
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }
                updateLoop(diff);
                realPrevTime = realCurrTime;
            }
            RUNNING = false;
            log.info("GameLogicHandleThread stop ...");
        });
    }

    /** 处理服务器帧：TrinityCore 中这里会把遍历每个玩家，将每个消息队列取出 100 个消息尽量去处理 */
    private void updateLoop(long deltaTime) {
        // 设置当前服务器帧间隔时间
        PDTime.DELTA = deltaTime;

        // 尝试从队列取出消息, 一帧最多处理 x 条消息
        for (Map.Entry<String, LinkedBlockingQueue<ParadogsContext>> entry : actorQueueMap.entrySet()) {
            String actorId = entry.getKey();
            LinkedBlockingQueue<ParadogsContext> actorQueue = entry.getValue();

            // 判断 actor 是否正在处理中
            if (actorQueueLockMap.getOrDefault(actorId, false)) {
                continue;
            }

            // 尝试获取下一条消息
            ParadogsContext ctx = actorQueue.poll();
            if (ctx == null) { continue; }

            /** 处理消息 */
            actorQueueLockMap.put(actorId, true);
            virtualThreadExecutor.submit(() -> {
                long t = System.currentTimeMillis();
                try {
                    ContextUtils.setContext(ctx); // 设置上下文
                    if (ctx.getExec() == null) {
                        // 普通路由消息
                        handleByRoute(ctx);
                    } else {
                        // 方法类型消息（回调等）
                        ctx.getExec().run();
                    }
                } catch (Exception e) {
                    // 转发到全局异常处理
                    ParadogsExceptionHandlerManager.forward(e, ctx);
                } finally {
                    // 发布事件
                    try {
                        if (ctx != null && ctx.getExec() == null) {
                            EventUtils.publishEvent(new ServerEvent.OneMessageHandleAfterEvent(ctx));
                        }
                    } catch (Exception e) {
                        // 防止报错中断处理队列
                        log.error("{}", e);
                    }

                    // 释放上下文
                    ContextUtils.release();
                    
                    // 释放锁定
                    actorQueueLockMap.put(actorId, false);

                    // 监听耗时
                    if (System.currentTimeMillis() - t > 10) {
                        log.warn("[{}]消息处理耗时：{}ms>10", ctx.getRoute(), System.currentTimeMillis() - t);
                    }
                }
            });

        }

        // 服务器帧更新
        try {
            EventUtils.publishEvent(SERVER_UPDATE_EVENT);
        } catch (Exception e) {
            log.error("Server update err: {}", e.getMessage(), e);
        }
    }

    /** 停止线程 */
    public static void stop() {
        RUNNING_FLAG = false;
        ParadogsGameLogicHandleThread.join("stop", () -> {}); // 激活阻塞队列
//        virtualThreadExecutor.shutdown();
    }

    /** 线程是否完全停止 */
    public static boolean isAlreadyStop() {
        return !RUNNING;
    }



}
